using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using SavedVariablesParser;

namespace SV2Xml {
    public partial class MainForm : Form {
        private delegate void SetTextCallback(string text);
        private delegate void SetCursorStateCallback(Cursor cursor);
        private delegate void IncrementProgressBarCallback();
        private delegate void SetStatusStateCallback(string state);
        private delegate void SetStatusMessageCallback(string state);
        private delegate void ResetProgressBarCallback();
        private delegate void SetSaveAsXMLMenuItemStatusCallback(bool enabled);
        private Document addonDocument;

        public MainForm() {
            InitializeComponent();
        }

        private void openFileDialog1_FileOk(object sender, CancelEventArgs e) {
            if (!e.Cancel) {
                this.ProcessDocument(this.openFileDialog.FileName);
            }
        }

        private void ProcessDocument(string path) {
            if (!this.openFileBackgroundWorker.IsBusy) {
                this.convertedText.Visible = true;
                this.mostRecentlyUsedFileMenu.AddFile(path);
                this.addonDocument = new SavedVariablesParser.Document(path);
                this.addonDocument.LineParsed += new SavedVariablesParser.Document.LineParsedEventHandler(parser_LineParsed);
                this.toolStripStatusBar.Maximum = this.addonDocument.LineCount;

                this.SetSaveAsXMLMenuItemStatus(false);
                this.SetSaveXMLMenuItemStatus(false);
                this.SetTextboxText(string.Empty);
                this.SetCursorState(Cursors.WaitCursor);
                this.SetStatusState("Opening " + this.addonDocument.AddonName);
                this.SetStatusMessage(string.Empty);
                this.statusStrip1.Update();

                this.openFileBackgroundWorker.RunWorkerAsync(this.addonDocument);
            } else {
                this.SetTextboxText("The background procesor is still processing");
                this.openFileBackgroundWorker.CancelAsync();
            }
        }

        void parser_LineParsed(SavedVariablesParser.Document sender, SavedVariablesParser.Line.ILine line) {
            this.IncrementProgressBar();
        }

        private void openFile_Click(object sender, EventArgs e) {
            this.openFileDialog.ShowDialog();
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e) {
            this.openFileDialog.ShowDialog();
        }

        private void openFileBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
            string xml = ((SavedVariablesParser.Document)e.Argument).ConvertToXML();
            e.Result = xml;
            System.Threading.Thread.Sleep(250);
        }

        private void openFileBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
            if (e.Error != null) {
                // there was an error.
                this.SetTextboxText("Open operation failed, the message was:" + Environment.NewLine + e.Error.Message);
                this.SetStatusMessage("Open operation failed");
                this.SetSaveXMLMenuItemStatus(false);
                this.SetSaveAsXMLMenuItemStatus(false);
            } else if (e.Cancelled) {
                // the operation was cancelled.
                this.SetTextboxText("Open operation cancelled");
                this.SetStatusMessage("Open operation cancelled");
                this.SetSaveXMLMenuItemStatus(false);
                this.SetSaveAsXMLMenuItemStatus(false);
            } else {
                // the operation was a success.
                this.SetTextboxText(e.Result.ToString());
                this.SetStatusMessage("Opened " + this.addonDocument.AddonName + " in " + this.addonDocument.Duration.ToString());
                this.SetSaveXMLMenuItemStatus(true);
                this.SetSaveAsXMLMenuItemStatus(true);
            }
            this.SetCursorState(Cursors.Default);
            this.SetStatusState("Idle");
            this.ResetProgressBar();
        }

        private void IncrementProgressBar() {
            if (this.statusStrip1.InvokeRequired) {
                IncrementProgressBarCallback callback = new IncrementProgressBarCallback(IncrementProgressBar);
                this.Invoke(callback, new object[] { });
            } else {
                this.toolStripStatusBar.PerformStep();
            }
        }

        private void ResetProgressBar() {
            if (this.statusStrip1.InvokeRequired) {
                ResetProgressBarCallback callback = new ResetProgressBarCallback(ResetProgressBar);
                this.Invoke(callback, new object[] { });
            } else {
                this.toolStripStatusBar.Value = 0;
            }
        }

        private void SetStatusState(string state) {
            if (this.statusStrip1.InvokeRequired) {
                SetStatusStateCallback callback = new SetStatusStateCallback(SetStatusState);
                this.Invoke(callback, new object[] { state });
            } else {
                this.toolStripStatusState.Text = state;
            }
        }

        private void SetStatusMessage(string message) {
            if (this.statusStrip1.InvokeRequired) {
                SetStatusMessageCallback callback = new SetStatusMessageCallback(SetStatusMessage);
                this.Invoke(callback, new object[] { message });
            } else {
                this.toolStripStatusMessage.Text = message;
            }
        }

        private void SetTextboxText(string text) {
            // InvokeRequired required compares the thread ID of the
            // calling thread to the thread ID of the creating thread.
            // If these threads are different, it returns true.
            if (this.convertedText.InvokeRequired) {
                SetTextCallback d = new SetTextCallback(SetTextboxText);
                this.Invoke(d, new object[] { text });
            } else {
                this.convertedText.Text = text;
            }
        }

        private void SetSaveXMLMenuItemStatus(bool enabled) {
            if (this.mainMenu.InvokeRequired) {
                SetSaveAsXMLMenuItemStatusCallback callback = new SetSaveAsXMLMenuItemStatusCallback(SetSaveAsXMLMenuItemStatus);
                this.Invoke(callback, new object[] { enabled });
            } else {
                this.saveToolStripMenuItem.Enabled = enabled;
            }
        }

        private void SetSaveAsXMLMenuItemStatus(bool enabled) {
            if (this.mainMenu.InvokeRequired) {
                SetSaveAsXMLMenuItemStatusCallback callback = new SetSaveAsXMLMenuItemStatusCallback(SetSaveAsXMLMenuItemStatus);
                this.Invoke(callback, new object[] { enabled });
            } else {
                this.saveAsToolStripMenuItem.Enabled = enabled;
            }
        }

        private void SetCursorState(Cursor cursor) {
            if (this.convertedText.InvokeRequired) {
                SetCursorStateCallback callback = new SetCursorStateCallback(SetCursorState);
                this.Invoke(callback, new object[] { cursor });
            } else {
                this.convertedText.Cursor = cursor;
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e) {
            AboutBox about = new AboutBox();
            about.Show();
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e) {
            this.SaveFile(string.Empty);
        }

        private void setSavedVariablesDirectoryToolStripMenuItem_Click(object sender, EventArgs e) {
            Preferences preferences = new Preferences();
            preferences.Show();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e) {
            Application.Exit();
        }

        private void saveFileBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
            if (e.Argument == null) {
                e.Result = this.addonDocument.SaveConvertedXML();
            } else {
                this.addonDocument.SaveConvertedXML(e.Argument.ToString());
                e.Result = e.Argument.ToString();
            }
        }

        private void saveFileBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
            this.SetCursorState(Cursors.Default);
            this.SetStatusState("Idle");
            this.SetTextboxText(this.addonDocument.AddonName + " saved as " + e.Result);
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e) {
            this.saveAsFileDialog.FileName = this.addonDocument.AddonName + ".xml";
            this.saveAsFileDialog.ShowDialog();
        }

        private void saveAsFileDialog_FileOk(object sender, CancelEventArgs e) {
            this.SaveFile(this.saveAsFileDialog.FileName);
        }

        private void SaveFile(string path) {
            this.SetCursorState(Cursors.WaitCursor);
            this.SetStatusState("Saving...");
            this.SetStatusMessage(string.Empty);

            if (string.IsNullOrEmpty(path)) {
                this.saveFileBackgroundWorker.RunWorkerAsync();
            } else {
                this.saveFileBackgroundWorker.RunWorkerAsync(path);
            }
        }

        private void selectAddonsToolStripMenuItem_Click(object sender, EventArgs e) {
            this.batchFileOpenDialog.ShowDialog();
        }

        #region batch processing

        private void batchFileOpenDialog_FileOk(object sender, CancelEventArgs e) {
            if (!e.Cancel) {
                this.convertedText.Visible = true;

                this.SetTextboxText(string.Empty);

                foreach (string fileName in this.batchFileOpenDialog.FileNames) {
                    DocumentProcessor.Instance.Add(new Document(fileName));
                    this.mostRecentlyUsedFileMenu.AddFile(fileName);
                }

                this.SetCursorState(Cursors.WaitCursor);

                DocumentProcessor.Instance.DocumentCompleted += new DocumentProcessor.DocumentCompletedEventHandler(Instance_DocumentCompleted);
                DocumentProcessor.Instance.DocumentStarted += new DocumentProcessor.DocumentStartedEventHandler(Instance_DocumentStarted);
                this.AppendTextToTextBox("Processing Addons");
                this.AppendTextToTextBox(Environment.NewLine);
                this.SetStatusMessage(string.Empty);
                this.batchConversionBackgroundWorker.RunWorkerAsync(DocumentProcessor.Instance);

                this.SetCursorState(Cursors.Default);
                this.toolStripStatusBar.Maximum = DocumentProcessor.Instance.Count;
                this.SetStatusState("Processing");
            }
        }

        private void Instance_DocumentStarted(object sender, Document document) {
            this.AppendTextToTextBox(Environment.NewLine);
            this.AppendTextToTextBox("Begin Processing:  ");
            this.AppendTextToTextBox(document.AddonName);
            this.AppendTextToTextBox(Environment.NewLine);
        }

        private void Instance_DocumentCompleted(object sender, DocumentProcessor.DocumentCompletedEventArgs e) {
            this.AppendTextToTextBox("Completed Processing:  ");
            this.AppendTextToTextBox(e.Document.AddonName);
            this.AppendTextToTextBox(" [");
            this.AppendTextToTextBox(e.SavePath);
            this.AppendTextToTextBox("] ");
            this.AppendTextToTextBox(e.Document.Duration.ToString());
            this.AppendTextToTextBox(Environment.NewLine);

            this.IncrementProgressBar();
            //    // TODO:  check cursor wait when opening a file.  for some reason it currently is inconsistent.
        }

        private void batchConversionBackgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
            ((DocumentProcessor)e.Argument).Process();
        }

        private void batchConversionBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
            if (e.Error != null) {
                // there was an error.
                this.AppendTextToTextBox("Batch conversion operation failed, the message was:" + Environment.NewLine + e.Error.Message);
                this.SetStatusMessage("Batch conversion operation failed");
                this.SetSaveXMLMenuItemStatus(false);
                this.SetSaveAsXMLMenuItemStatus(false);
            } else if (e.Cancelled) {
                // the operation was cancelled.
                this.AppendTextToTextBox("Batch conversion operation cancelled");
                this.SetStatusMessage("Batch conversion operation cancelled");
                this.SetSaveXMLMenuItemStatus(false);
                this.SetSaveAsXMLMenuItemStatus(false);
            } else {
                // the operation was a success.
                this.AppendTextToTextBox(Environment.NewLine);
                this.AppendTextToTextBox("Completed Batch Conversion Operation");
                this.SetStatusMessage("Completed Batch Conversion Operation");
                this.SetSaveXMLMenuItemStatus(true);
                this.SetSaveAsXMLMenuItemStatus(true);
            }
            this.SetCursorState(Cursors.Default);
            this.SetStatusState("Idle");
            this.ResetProgressBar();
        }

        private delegate void AppendTextToTextBoxCallback(string message);
        private void AppendTextToTextBox(string message) {
            if (this.convertedText.InvokeRequired) {
                AppendTextToTextBoxCallback callback = new AppendTextToTextBoxCallback(AppendTextToTextBox);
                this.Invoke(callback, new object[] { message });
            } else {
                this.convertedText.Text += message;
            }
        }

        #endregion

        private void mostRecentlyUsedFileMenu_FileClicked(object sender, string path) {
            this.ProcessDocument(path);
        }
    }
}